<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="/ui/vendor/jquery/jquery-2.2.3.min.js"></script>
    <script src="/ui/vendor/tableau/tableauwdc-1.1.0.js"></script>
    <script src="drds-client.js" type="text/javascript"></script>

    <script type="text/javascript">
        var headersPushed = false;
        var statementClient = null;
        var getTableDataCalled = false;
        var buffer = [];
        var schema = null;
        var drdsWebConnector = tableau.makeConnector();
        var errorName = null;
        var errorMessage = null;

        function pushHeadersToTableau(columns) {
            tableau.log('pushHeadersToTableau called');
            if (columns && !headersPushed) {
                schema = columns;
                var fieldTypes = []
                var fieldNames = []
                for (var i = 0; i < columns.length; i++) {
                    var columnType = columns[i].type;
                    switch (columnType) {
                        case 'boolean':
                            columnType = 'bool';
                            break;
                        case 'date':
                            columnType = 'date';
                            break;
                        default:
                            columnType = 'string';
                    }
                    fieldNames.push(columns[i].name);
                    fieldTypes.push(columnType);
                }
                tableau.headersCallback(fieldNames, fieldTypes);
                headersPushed = true;
                tableau.log('pushHeadersToTableau done');
            }
        }

        function isComplexType(typeName) {
            return (typeName.indexOf('map') === 0) ||
                (typeName.indexOf('array') === 0) ||
                (typeName.indexOf('row') === 0);
        }

        function pushDataToTableau(data, columns, lastRecordNumber) {
            tableau.log('pushDataToTableau called ' + data.length);
            if (data.length > 0) {
                if (!getTableDataCalled) {
                    buffer.push(data);
                    return;
                } else {
                    var tableauData = [];
                    for (var i = 0; i < data.length; i++) {
                        var dataArray = data[i];
                        var row = new Object();
                        for (var j = 0; j < columns.length; j++) {
                            // since Tableau doesn't support complex types convert them to json strings
                            // so at least they get rendered
                            if (isComplexType(columns[j].type)) {
                                row[columns[j].name] = JSON.stringify(dataArray[j]);
                            } else {
                                row[columns[j].name] = dataArray[j];
                            }
                        }
                        tableauData.push(row);
                    }
                    tableau.dataCallback(tableauData, lastRecordNumber, true);
                }
            }
        }

        function errorHandler(errName, errMessage) {
            errorName = errName;
            errorMessage = errMessage;
        }

        drdsWebConnector.getColumnHeaders = function () {
            tableau.log('getColumnHeaders called');
            statementClient = new StatementClient(JSON.parse(tableau.connectionData), pushHeadersToTableau, pushDataToTableau, errorHandler);
            // Tableau waits for the headers before calling getTableData()
            while (!(headersPushed || errorMessage)) {
                statementClient.advance(0);
            }

            if (errorMessage) {
                var message = "Error: [" + errorName + "]: " + errorMessage;
                tableau.log(message);
                tableau.abortWithError(message);
                return;
            }

            tableau.log('getColumnHeaders done');
        }

        drdsWebConnector.getTableData = function (lastRecordNumber) {
            tableau.log('getTableData called');
            getTableDataCalled = true;
            var data = [];

            // we have some buffered data due to advance() calls in getColumnHeaders()
            tableau.log('buffer.length ' + buffer.length);
            if (buffer.length > 0) {
                for (var i = 0; i < buffer.length; i++) {
                    // exhaust the buffer first
                    tableau.log('exhausting the buffer first');
                    pushDataToTableau(buffer.splice(i, 1)[0], schema, lastRecordNumber);
                    return;
                }
            }

            tableau.log('getting next result page from polardbx');
            statementClient.advance(lastRecordNumber);

            if (!statementClient.valid) {
                tableau.log('done pulling the result set');
                tableau.dataCallback([], lastRecordNumber, false);
                return;
            }
            tableau.log('getTableData done');
        }

        tableau.registerConnector(drdsWebConnector);

        $(document).ready(function () {

            var sessionParamsDiv = $('#sessionParameters');
            var sessionParameterCount = 0;

            function populateCatalogs() {
                var catalogClient = new StatementClient({
                        'query': 'show catalogs'
                    },
                    function () {
                    },
                    function (data, columns) {
                        var catalogSelect = document.getElementById("catalog");
                        for (var i = 0; i < data.length; i++) {
                            var catalog = data[i];
                            var opt = document.createElement("option");
                            opt.textContent = catalog;
                            opt.value = catalog;
                            catalogSelect.appendChild(opt);
                        }
                    },
                    errorHandler
                );

                while (catalogClient.valid) {
                    catalogClient.advance(0);
                }
            }

            function populateSchemas(catalogName) {
                // remove the existing options
                var schemaSelect = document.getElementById("schema");
                for (i = schemaSelect.options.length - 1; i >= 0; i--) {
                    schemaSelect.remove(i);
                }

                var schemaClient = new StatementClient({
                        'catalog': catalogName,
                        'schema': 'default',
                        'query': 'show schemas'
                    },
                    function () {
                    },
                    function (data, columns) {
                        for (var i = 0; i < data.length; i++) {
                            var schema = data[i];
                            var opt = document.createElement("option");
                            opt.textContent = schema;
                            opt.value = schema;
                            if (schema == 'default') {
                                opt.setAttribute("selected", "true");
                            }
                            schemaSelect.appendChild(opt);
                        }
                    },
                    errorHandler
                );
                while (schemaClient.valid) {
                    schemaClient.advance(0);
                }
            }

            populateCatalogs();
            // initially load the schemas of the first catalog
            populateSchemas($('#catalog option:selected').val());

            document.getElementById("catalog").addEventListener('change',
                function () {
                    populateSchemas(this.value);
                }
                , false);

            function addSessionParameterToForm(name, value) {
                $('<p><label for="sessionParameters"><input type="text" id="name_' + sessionParameterCount + '" size="30" value="' + name + '" placeholder="name"/>'
                    + '<input type="text" id="value_' + sessionParameterCount + '" size="20" value="' + value + '" placeholder="value" /></label> '
                    + '<a href="#" id="removeSessionProp_' + sessionParameterCount + '">X</a></p>').appendTo(sessionParamsDiv);

                $('#removeSessionProp_' + sessionParameterCount).click(function () {
                    $(this).parents('p').remove();
                    sessionParameterCount--;
                    return false;
                });

                sessionParameterCount++;
            }

            if (tableau.connectionData) {
                var existingConnectionData = JSON.parse(tableau.connectionData);
                $('textarea#query').val(existingConnectionData.query);
                $('input#user_name').val(existingConnectionData.userName);

                if (existingConnectionData.sessionParameters) {
                    var parameterMap = JSON.parse(existingConnectionData.sessionParameters);
                    for (var name in parameterMap) {
                        var value = parameterMap[name];
                        addSessionParameterToForm(name, value);
                    }
                }
            }

            if (tableau.connectionName) {
                $('input#dsname').val(tableau.connectionName);
            }

            $("#addSessionProp").click(function () {
                addSessionParameterToForm('', '');
                return false;
            });

            $("#inputForm").submit(function () {
                // start with a new client
                statementClient = null;

                // clear global state
                headersPushed = false;
                tableau.connectionData = null;

                // create a map of session parameters { name->value }
                var i = 0;
                var paramName;
                var sessionParameters = {};
                $('#sessionParameters p label input').each(function () {
                    if (i == 0) {
                        // name
                        paramName = this.value;
                        sessionParameters[paramName] = null;
                        i++;
                    }
                    else if (i == 1) {
                        // value
                        sessionParameters[paramName] = this.value;
                        i = 0;
                    }
                });

                tableau.log(sessionParameters);

                var query = $('textarea#query').val();
                var schema = $('select#schema').val();
                var catalog = $('select#catalog').val();
                var userName = $('input#user_name').val();
                var dataSourceName = $('input#dsname').val();

                var connectionData = {
                    'catalog': catalog,
                    'schema': schema,
                    'query': query,
                    'userName': userName,
                    'sessionParameters': JSON.stringify(sessionParameters)
                };

                // set these before validation, because if validation fails
                // the form state is recovered from these variables
                tableau.connectionData = JSON.stringify(connectionData);
                tableau.connectionName = dataSourceName;

                if (!query || query.length == 0 ||
                    !schema || schema.length == 0 ||
                    !catalog || catalog.length == 0 ||
                    !userName || userName.length == 0 ||
                    !dataSourceName || dataSourceName.length == 0) {
                    alert('Please fill in all the fields');
                    return;
                }

                tableau.submit();
            });
        });
    </script>
</head>

<body>
<h2>PolarDBX Tableau Web Connector</h2>
<br>
<form id="inputForm" action="">
    User Name:
    <input type="text" id="user_name"><br><br>

    Catalog:
    <select id="catalog"></select><br><br>

    Schema:
    <select id="schema"></select><br><br>

    Data Source Name:
    <input type="text" id="dsname"><br><br>

    <a href="#" id="addSessionProp">Add Session Parameter</a>
    <div id="sessionParameters"></div>

    <br><br>Please enter your SQL query:<br>
    <textarea id="query" form="inputForm" rows="20" cols="100"></textarea>

    <br>
    <input type="submit" value="Submit">
</form>
</body>
</html>
